﻿#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <time.h>
#include <bitset>
#include <vector>
#include "header.h"

#pragma comment (lib, "ws2_32.lib")
#pragma warning (once : 4996)

#ifndef SIO_RCVALL
#define SIO_RCVALL    0x98000001
#endif

#ifndef IP_HDRINCL
#define IP_HDRINCL    0x2
#endif

#define uint8_t  unsigned char
#define uint16_t unsigned short
#define uint32_t unsigned int
#define uint64_t unsigned long long

#define IpHeaderLength 20
#define ICMPHeaderLength 20
#define IGMPHeaderLength  8
#define TCPHeaderLength  20
#define UDPHeaderLength   8

#define USE_LOG		1
#define LOGNAME		"tcpdump.log"

std::bitset<10000> portHash;

int Error(LPCSTR message)
{
	WSACleanup();
	printf("%s%s\nQuitting, press any key to continue ...",message,"\n\n");
	getchar();

	return WSAGetLastError();
}

char* DottedDecimal(long lAddr)
{
	in_addr addr;
	addr.S_un.S_addr=lAddr;
	return inet_ntoa(addr);
}

std::vector<uint32_t> GetLocalAddress()
{
	HOSTENT*    pNormalHostEnt;
	char        cHostName[0xFF] = {0};
	std::vector<uint32_t> ret;

	if (gethostname(cHostName,0xFF)==SOCKET_ERROR) return ret;
	if (!(pNormalHostEnt = gethostbyname(cHostName))) return ret;
	for (int i = 0; pNormalHostEnt->h_addr_list[i]; i ++) {
		ret.push_back(*(uint32_t*)pNormalHostEnt->h_addr_list[i]);
	}
	return ret;
}


void InitPassPort()
{
	portHash.reset();
	//portHash[80] = true;
	portHash[443] = true;
	portHash[808] = true;
	portHash[1080] = true;
	portHash[8080] = true;
}


void InitSockBind(SOCKET sock, uint32_t addr)
{
	BOOL			bIsTrue  = TRUE;
	uint32_t		ulBytes;
	sockaddr_in		soSrc;
	memset(&soSrc,0,sizeof(soSrc));

	soSrc.sin_family       =  AF_INET;
	soSrc.sin_port         =  htons ( 0 );
	soSrc.sin_addr.s_addr  =  addr;

	if(setsockopt(sock,IPPROTO_IP,IP_HDRINCL, (char*)
		&bIsTrue, sizeof(bIsTrue)) == SOCKET_ERROR)
	{
		Error("Unable to set \"Header Included\""
			"flag. Quitting."); 
		return;
	}

	if (bind(sock,(sockaddr*)&soSrc,sizeof(soSrc)))
	{
		Error("Could not bind socket.");
		return;
	}

	u_long mode = 0;
	::ioctlsocket(sock, FIONBIO, &mode);

	if (WSAIoctl(sock,SIO_RCVALL,&bIsTrue,sizeof(bIsTrue),
		NULL,0,(LPDWORD)&ulBytes,NULL,NULL))
	{
		Error("Unable to start sniffer!");
		return;
	}
}



int main(int argc, char* argv[])
{
	WSADATA          wsa;
	OSVERSIONINFO    ver;

	printf("Welcome using this applet. ^_^\n");

#if USE_LOG
	freopen(LOGNAME, "ab+", stdout);
	printf("=================\n");
	__time64_t ltime;
	_time64(&ltime);
	printf("The time is %s\n", _ctime64(&ltime));
#endif
	
	if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
		return Error("Windows Sockets initialization failed.");

	if ((LOBYTE(wsa.wVersion) < 2) || (HIBYTE(wsa.wVersion) < 2))
	{
		char err[50] = {0};
		sprintf(err,"Winsock DLL Version %d.%d found, "
			"at least Version 2.2 is required.",
			LOBYTE(wsa.wVersion),
			HIBYTE(wsa.wVersion)
			);

		return Error(err);
	}

	ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	if(GetVersionEx((LPOSVERSIONINFO) &ver))
	{
		if (!((ver.dwPlatformId > 1) && (ver.dwMajorVersion == 5)))
		{
			return Error("This program only works "
				"on Windows 2000, XP and later OS.");
		}
	}

	else
	{
		return Error("OS Version could not be "
			"determined. Quitting.");
	}

	InitPassPort();

	//////////////////////////////////////////////////////////////////////////
	std::vector<uint32_t> netCards = GetLocalAddress();
	if (netCards.empty())
		return 0;

	SOCKET*	sSockRaw = new SOCKET[netCards.size()];
	for (int i = 0; i < netCards.size(); i ++)
	{
		sSockRaw[i] = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
		InitSockBind(sSockRaw[i], netCards[i]);
	}

	printf("Initialization successful. Press any key to "
		"start sniffing, and use [CTRL]-[C] to cancel "
		"the program.");

	for (int i = 0; true; i = (i+1)%netCards.size())
	{
		char vBuffer[0xBAD] = {0};
		sockaddr saConnected;
		int iTmp = sizeof(saConnected);
		int iResult=recvfrom(sSockRaw[i],vBuffer,0xBAD,NULL,
			&saConnected,&iTmp);

		if (iResult!=SOCKET_ERROR)
		{
			ip_hdr* pIPhdr = (ip_hdr*)vBuffer;
			
			int  tProtLen = IpHeaderLength;
			char sProtocol[0x10] = {0};
			char sFlag[0x20] = {0};

			pIPhdr->tot_len = ::ntohs(pIPhdr->tot_len);

			switch(pIPhdr->protocol)
			{
			case IPPROTO_ICMP:
				tProtLen = ICMPHeaderLength;
				sprintf(sProtocol,"ICMP");
				break;
			case IPPROTO_IGMP:
				tProtLen = ICMPHeaderLength;
				sprintf(sProtocol,"ICMP");
				break;
			case IPPROTO_TCP:
				tProtLen = TCPHeaderLength;
				sprintf(sProtocol,"TCP");
				break;
			case IPPROTO_UDP:
				tProtLen = UDPHeaderLength;
				sprintf(sProtocol,"UDP");
				break;
			default:
				continue;
			}

			if (pIPhdr->protocol != IPPROTO_TCP)
				continue;

			tcp_hdr* pTCPhdr = (tcp_hdr*)(vBuffer + pIPhdr->ihl * 4);
			pTCPhdr->src_port = ::ntohs(pTCPhdr->src_port);
			pTCPhdr->dst_port = ::ntohs(pTCPhdr->dst_port);

			if (pIPhdr->protocol != IPPROTO_TCP 
				|| (portHash[pTCPhdr->src_port] != true && portHash[pTCPhdr->dst_port] != true))
				continue;

			if (pTCPhdr->fin)
				::strcat(sFlag, " FIN");
			if (pTCPhdr->syn)
				::strcat(sFlag, " SYN");
			if (pTCPhdr->rst)
				::strcat(sFlag, " RST");
			if (pTCPhdr->psh)
				::strcat(sFlag, " PSH");
			if (pTCPhdr->ack)
				::strcat(sFlag, " ACK");
			if (pTCPhdr->urg)
				::strcat(sFlag, " URG");

			printf
				(
				"\n%s\t%08d\t[TTL=%3d] [%s:%d -> %s:%d]%s",
				sProtocol,
				clock(),
				pIPhdr->ttl,
				DottedDecimal(pIPhdr->srcaddr.s_addr),
				pTCPhdr->src_port,
				DottedDecimal(pIPhdr->dstaddr.s_addr),
				pTCPhdr->dst_port,
				sFlag
				);
			
			if (iResult > (pIPhdr->ihl * 4 + pTCPhdr->thl * 4))
			{
				char* pdata = (char*)pTCPhdr + pTCPhdr->thl * 4;
				int idataLen = iResult - (pIPhdr->ihl * 4 + pTCPhdr->thl * 4);
#if USE_LOG
				fwrite("\n", 1, 1, stdout);
				fwrite(pdata, 1, idataLen, stdout);
				fwrite("\n", 1, 1, stdout);
#endif
			}
			
#if USE_LOG
			fflush(stdout);
#endif
		}
		else
		{
			WSAGetLastError();
			WSASetLastError(0);
		}
	}

	delete [] sSockRaw;

	return 0;
}